package org.vison.wonfu.framework.soa.spi;

import lombok.SneakyThrows;
import org.slf4j.Logger;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
import org.springframework.beans.factory.support.GenericBeanDefinition;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
import org.springframework.core.type.filter.AnnotationTypeFilter;
import org.springframework.util.StringUtils;
import org.vison.wonfu.framework.soa.SoaConstants;
import org.vison.wonfu.framework.soa.annotation.SoaService;
import org.vison.wonfu.framework.soa.governance.MetaData;
import org.vison.wonfu.framework.soa.governance.SoaProviderMeta;

import java.lang.reflect.Method;
import java.util.*;

/**
 * SOA服务扫描器，在Bean定义注册阶段扫描并注册SOA服务
 * 其执行时机非常早（在 BeanFactoryPostProcessor 之前，早于所有 Bean 实例化），因此能注入的 Bean 非常有限
 */
@Configuration
public class SoaServiceScanner implements BeanDefinitionRegistryPostProcessor, ApplicationContextAware {

    private final Logger logger = org.slf4j.LoggerFactory.getLogger(SoaServiceScanner.class);


    private final Environment environment;
    private ApplicationContext applicationContext;
    private final Map<String, ServiceDefinition> serviceDefinitionMap = new HashMap<>();

    public SoaServiceScanner(Environment environment) {
        this.environment = environment;
    }

    @SneakyThrows
    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
        String scanPackages = environment.getProperty(SoaConstants.SOA_SERVICE_SCANNER);
       if(!StringUtils.hasText(scanPackages)) {
           throw new IllegalArgumentException("未配置扫描包路径");
       }
       // 按分号分隔多个包路径,转成数组
        String[] packages = scanPackages.split(";");

        ClassPathScanningCandidateComponentProvider scanner =
                new ClassPathScanningCandidateComponentProvider(false);
        scanner.addIncludeFilter(new AnnotationTypeFilter(SoaService.class));

        // 扫描指定包下的组件
        Set<BeanDefinition> components = new HashSet<>();
        for (String pkg : packages) {
            components.addAll(scanner.findCandidateComponents(pkg));
        }
        for (BeanDefinition bd : components) {
            Class<?> serviceClass = Class.forName(bd.getBeanClassName());
            SoaService soaServiceAnnotation = serviceClass.getAnnotation(SoaService.class);
            String serviceName = StringUtils.hasText(soaServiceAnnotation.path()) ?
                    soaServiceAnnotation.path() : serviceClass.getSimpleName();
            String servicePath = soaServiceAnnotation.path();
            if (!StringUtils.hasText(servicePath)) {
                servicePath = "/" + serviceName; // 默认使用服务名作为路径
            } else if (!servicePath.startsWith("/")) {
                servicePath = "/" + servicePath; // 确保路径以斜杠开头
            }

            // 注册服务Bean
            registerServiceBean(registry, serviceClass);
            Class<?>[] interfaces = serviceClass.getInterfaces();
            if (interfaces.length == 0) {
                logger.info("警告: " + serviceClass.getName() + " 没有实现任何接口");
                continue;
            }

            Class<?> serviceInterface = interfaces[0];
            Map<String, Method> methodMap = new HashMap<>();

            for (Method method : serviceInterface.getMethods()) {
                methodMap.put(method.getName(), method);
                String providerKey = MetaData.buildProviderKey(serviceClass, method);
                MetaData.addProviderMeta(providerKey, SoaProviderMeta.builder()
                        .commandKey(providerKey).providerClass(serviceClass.getName())
                        .method(method.getName())
                        .servicePath(servicePath)
                        .build());
            }
            serviceDefinitionMap.put(serviceName, new ServiceDefinition(
                    serviceClass, serviceName, servicePath, methodMap
            ));
        }
    }

    private void registerServiceBean(BeanDefinitionRegistry registry, Class<?> serviceClass) {
        if (!registry.containsBeanDefinition(serviceClass.getName())) {
            GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
            beanDefinition.setBeanClass(serviceClass);
            beanDefinition.setAutowireMode(GenericBeanDefinition.AUTOWIRE_BY_TYPE);
            registry.registerBeanDefinition(serviceClass.getName(), beanDefinition);
        }
    }

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        // 可以在这里进行额外的BeanFactory处理
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }

    public ApplicationContext getApplicationContext() {
        return applicationContext;
    }

    public Map<String, ServiceDefinition> getServiceDefinitionMap() {
        return serviceDefinitionMap;
    }

    public static class ServiceDefinition {
        private final Class<?> serviceClass;
        private final String serviceName;
        private final String servicePath;
        private final Map<String, Method> methodMap;



        public ServiceDefinition(Class<?> serviceClass, String serviceName, String servicePath,
                                 Map<String, Method> methodMap) {
            this.serviceClass = serviceClass;
            this.serviceName = serviceName;
            this.servicePath = servicePath;
            this.methodMap = methodMap;
        }

        public Class<?> getServiceClass() {
            return serviceClass;
        }

        public String getServiceName() {
            return serviceName;
        }

        public String getServicePath() {
            return servicePath;
        }

        public Map<String, Method> getMethodMap() {
            return methodMap;
        }
    }
}